/**
 * 工具类
 * 部分文档地址
 * https://uviewui.com/js/intro.html
 */

export default class tools{
	/*
		showToast  显示消息提示框。
	*/
	showToast(title: string, icon: string = "none", time: number = 1500): void {
	    uni.showToast({
	        title: title,
	    	icon:icon,
	        duration: time
	    });
	}
	toLogin(){
		uni.showModal({
			title: '暂未登录',
			content: '是否去登录',
			success: function(res) {
				if (res.confirm) {
					uni.navigateTo({
						url: '/pages/tabbar/login'
					})
				} else if (res.cancel) {
					return
				}
			}
		});
	}
	// 重新登录
	toLoginagin(){
		uni.showModal({
			title: '已在其他设备登录',
			content: '是否重新登录',
			success: function(res) {
				if (res.confirm) {
					uni.navigateTo({
						url: '/pages/tabbar/login'
					})
				} else if (res.cancel) {
					return
				}
			}
		});
	}
	/**
	 * 路由跳转
	 * 注意:本方法没有对跳转的回调函数进行封装
	 */
	route(options: object = {}, params: object = false) {
		let config = {
			type: 'navigateTo',
			url: '',
			delta: 1, // navigateBack页面后退时,回退的层数
			params: {}, // 传递的参数
			animationType: 'pop-in', // 窗口动画,只在APP有效
			animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
		};
		config = Object.assign(config, options);
		// 如果url没有"/"开头，添加上，因为uni的路由跳转需要"/"开头
		if (config.url[0] != '/') config.url = '/' + config.url;
		// 判断是否有传递显式的参数,Object.keys转为数组并判断长度,switchTab类型时不能携带参数
		if (Object.keys(config.params).length && config.type != 'switchTab') {
			// 判断用户传递的url中，是否带有参数
			// 使用正则匹配，主要依据是判断是否有"/","?","="等，如“/page/index/index?name=mary"
			// 如果有url中有get参数，转换后无需带上"?"
			let query = '';
			if (/.*\/.*\?.*=.*/.test(config.url)) {
				// object对象转为get类型的参数
				query = this.queryParams(config.params, false);
				// 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
				config.url += "&" + query;
			} else {
				query = this.queryParams(config.params);
				config.url += query;
			}
		}
		// 简写形式，把url和参数拼接起来
		if (typeof options === 'string' && typeof params == 'object') {
			let query = '';
			if (/.*\/.*\?.*=.*/.test(options)) {
				// object对象转为get类型的参数
				query = this.queryParams(params, false);
				// 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
				options += "&" + query;
			} else {
				query = this.queryParams(params);
				options += query;
			}
		}
		// 判断是否一个字符串，如果是，直接跳转(简写法)
		// 如果是中情形，默认第二个参数为对象形式的参数
		if (typeof options === 'string') {
			if (options[0] != '/') options = '/' + options;
			return uni.navigateTo({
				url: options
			});
		}
		// navigateTo类型的跳转
		if (config.type == 'navigateTo' || config.type == 'to') {
			return uni.navigateTo({
				url: config.url,
				animationType: config.animationType,
				animationDuration: config.animationDuration,
			});
		}
		if (config.type == 'redirectTo' || config.type == 'redirect') {
			return uni.redirectTo({
				url: config.url,
			});
		}
		if (config.type == 'switchTab' || config.type == 'tab') {
			return uni.switchTab({
				url: config.url,
			});
		}
		if (config.type == 'reLaunch') {
			return uni.reLaunch({
				url: config.url
			});
		}
		if (config.type == 'navigateBack' || config.type == 'back') {
			return uni.navigateBack({
				delta: parseInt(config.delta ? config.delta : this.delta)
			});
		}
	}
	/**
	 * 对象转url参数
	 * @param {*} data,对象
	 * @param {*} isPrefix,是否自动加上"?"
	 */
	queryParams(data: object = {}, isPrefix: boolean = true, arrayFormat: string = 'brackets') {
		let prefix = isPrefix ? '?' : ''
		let _result = []
		if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets';
		for (let key in data) {
			let value = data[key]
			// 去掉为空的参数
			if (['', undefined, null].indexOf(value) >= 0) {
				continue;
			}
			// 如果值为数组，另行处理
			if (value.constructor === Array) {
				// e.g. {ids: [1, 2, 3]}
				switch (arrayFormat) {
					case 'indices':
						// 结果: ids[0]=1&ids[1]=2&ids[2]=3
						for (let i = 0; i < value.length; i++) {
							_result.push(key + '[' + i + ']=' + value[i])
						}
						break;
					case 'brackets':
						// 结果: ids[]=1&ids[]=2&ids[]=3
						value.forEach(_value => {
							_result.push(key + '[]=' + _value)
						})
						break;
					case 'repeat':
						// 结果: ids=1&ids=2&ids=3
						value.forEach(_value => {
							_result.push(key + '=' + _value)
						})
						break;
					case 'comma':
						// 结果: ids=1,2,3
						let commaStr = "";
						value.forEach(_value => {
							commaStr += (commaStr ? "," : "") + _value;
						})
						_result.push(key + '=' + commaStr)
						break;
					default:
						value.forEach(_value => {
							_result.push(key + '[]=' + _value)
						})
				}
			} else {
				_result.push(key + '=' + value)
			}
		}
		return _result.length ? prefix + _result.join('&') : ''
	}
	// 判断arr是否为一个数组，返回一个bool值
	isArray (arr: object) {
	    return Object.prototype.toString.call(arr) === '[object Array]';
	}
	// 深度克隆
	deepClone (obj: object) {
		// 对常见的“非”值，直接返回原来值
		if([null, undefined, NaN, false].includes(obj)) return obj;
	    if(typeof obj !== "object" && typeof obj !== 'function') {
			//原始类型直接返回
	        return obj;
	    }
	    var o = isArray(obj) ? [] : {};
	    for(let i in obj) {
	        if(obj.hasOwnProperty(i)){
	            o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
	        }
	    }
	    return o;
	}
	/**
	 * 验证电子邮箱格式
	 */
	email(value) {
		return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value);
	}
	
	/**
	 * 验证手机格式
	 */
	mobile(value) {
		return /^1[23456789]\d{9}$/.test(value)
	}
	
	/**
	 * 验证URL格式
	 */
	url(value) {
		return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
			.test(value)
	}
	
	/**
	 * 验证日期格式
	 */
	date(value) {
		return !/Invalid|NaN/.test(new Date(value).toString())
	}
	
	/**
	 * 验证ISO类型的日期格式
	 */
	dateISO(value) {
		return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
	}
	
	/**
	 * 验证十进制数字
	 */
	number(value) {
		return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
	}
	
	/**
	 * 验证整数
	 */
	digits(value) {
		return /^\d+$/.test(value)
	}
	
	/**
	 * 验证身份证号码
	 */
	idCard(value) {
		return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
			value)
	}
	
	/**
	 * 是否车牌号
	 */
	carNo(value) {
		// 新能源车牌
		const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/;
		// 旧车牌
		const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/;
		if (value.length === 7) {
			return creg.test(value);
		} else if (value.length === 8) {
			return xreg.test(value);
		} else {
			return false;
		}
	}
	
	/**
	 * 金额,只允许2位小数
	 */
	amount(value) {
		//金额，只允许保留两位小数
		return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value);
	}
	
	/**
	 * 中文
	 */
	chinese(value) {
		let reg = /^[\u4e00-\u9fa5]+$/gi;
		return reg.test(value);
	}
	
	/**
	 * 只能输入字母
	 */
	letter(value) {
		return /^[a-zA-Z]*$/.test(value);
	}
	
	/**
	 * 只能是字母或者数字
	 */
	enOrNum(value) {
		//英文或者数字
		let reg = /^[0-9a-zA-Z]*$/g;
		return reg.test(value);
	}
	
	/**
	 * 验证是否包含某个值
	 */
	contains(value, param) {
		return value.indexOf(param) >= 0
	}
	
	/**
	 * 验证一个值范围[min, max]
	 */
	 range(value, param) {
		return value >= param[0] && value <= param[1]
	}
	
	/**
	 * 验证一个长度范围[min, max]
	 */
	rangeLength(value, param) {
		return value.length >= param[0] && value.length <= param[1]
	}
	
	/**
	 * 是否固定电话
	 */
	landline(value) {
		let reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/;
		return reg.test(value);
	}
	
	/**
	 * 判断是否为空
	 */
	empty(value) {
		switch (typeof value) {
			case 'undefined':
				return true;
			case 'string':
				if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true;
				break;
			case 'boolean':
				if (!value) return true;
				break;
			case 'number':
				if (0 === value || isNaN(value)) return true;
				break;
			case 'object':
				if (null === value || value.length === 0) return true;
				for (var i in value) {
					return false;
				}
				return true;
		}
		return false;
	}
	
	/**
	 * 是否json字符串
	 */
	jsonString(value) {
		if (typeof value == 'string') {
			try {
				var obj = JSON.parse(value);
				if (typeof obj == 'object' && obj) {
					return true;
				} else {
					return false;
				}
			} catch (e) {
				return false;
			}
		}
		return false;
	}
	
	
	/**
	 * 是否数组
	 */
	array(value) {
		if (typeof Array.isArray === "function") {
			return Array.isArray(value);
		} else {
			return Object.prototype.toString.call(value) === "[object Array]";
		}
	}
	
	/**
	 * 是否对象
	 */
	object(value) {
		return Object.prototype.toString.call(value) === '[object Object]';
	}
	
	/**
	 * 是否短信验证码
	 */
	code(value, len = 6) {
		return new RegExp(`^\\d{${len}}$`).test(value);
	}
	
	/**
	 * 去除空格
	 */
	trim(str, pos = 'both') {
		if (pos == 'both') {
			return str.replace(/^\s+|\s+$/g, "");
		} else if (pos == "left") {
			return str.replace(/^\s*/, '');
		} else if (pos == 'right') {
			return str.replace(/(\s*$)/g, "");
		} else if (pos == 'all') {
			return str.replace(/\s+/g, "");
		} else {
			return str;
		}
	}
	
	// 获取父组件的参数，因为支付宝小程序不支持provide/inject的写法
	// this.$parent在非H5中，可以准确获取到父组件，但是在H5中，需要多次this.$parent.$parent.xxx
	getParent(name, keys) {
		let parent = this.$parent;
		// 通过while历遍，这里主要是为了H5需要多层解析的问题
		while (parent) {
			// 父组件
			if (parent.$options.name !== name) {
				// 如果组件的name不相等，继续上一级寻找
				parent = parent.$parent;
			} else {
				let data = {};
				// 判断keys是否数组，如果传过来的是一个数组，那么直接使用数组元素值当做键值去父组件寻找
				if(Array.isArray(keys)) {
					keys.map(val => {
						data[val] = parent[val] ? parent[val] : '';
					})
				} else {
					// 历遍传过来的对象参数
					for(let i in keys) {
						// 如果子组件有此值则用，无此值则用父组件的值
						// 判断是否空数组，如果是，则用父组件的值，否则用子组件的值
						if(Array.isArray(keys[i])) {
							if(keys[i].length) {
								data[i] = keys[i];
							} else {
								data[i] = parent[i];
							}
						} else if(keys[i].constructor === Object) {
							// 判断是否对象，如果是对象，且有属性，那么使用子组件的值，否则使用父组件的值
							if(Object.keys(keys[i]).length) {
								data[i] = keys[i];
							} else {
								data[i] = parent[i];
							}
						} else {
							// 只要子组件有传值，即使是false值，也是“传值”了，也需要覆盖父组件的同名参数
							data[i] = (keys[i] || keys[i] === false) ? keys[i] : parent[i];
						}
					}
				}
				return data;
			}
		}
	
		return {};
	}
	// 获取父组件的参数，因为支付宝小程序不支持provide/inject的写法
	// this.$parent在非H5中，可以准确获取到父组件，但是在H5中，需要多次this.$parent.$parent.xxx
	// 这里默认值等于undefined有它的含义，因为最顶层元素(组件)的$parent就是undefined，意味着不传name
	// 值(默认为undefined)，就是查找最顶层的$parent
	$parent(name = undefined) {
		let parent = this.$parent;
		// 通过while历遍，这里主要是为了H5需要多层解析的问题
		while (parent) {
			// 父组件
			if (parent.$options && parent.$options.name !== name) {
				// 如果组件的name不相等，继续上一级寻找
				parent = parent.$parent;
			} else {
				return parent;
			}
		}
		return false;
	}
	// 获取os()工具方法
	
	os() {
		return uni.getSystemInfoSync().platform;
	};
	
	// 获取sys()工具方法
	sys() {
		return uni.getSystemInfoSync();
	}

	
	/**
	 * 防抖原理：一定时间内，只有最后一次操作，再过wait毫秒后才执行函数
	 * 
	 * @param {Function} func 要执行的回调函数 
	 * @param {Number} wait 延时的时间
	 * @param {Boolean} immediate 是否立即执行 
	 * @return null
	 */
	
	static timeout = null;
	debounce(func, wait = 500, immediate = false) {
		// 清除定时器
		if (this.timeout !== null) clearTimeout(this.timeout);
		// 立即执行，此类情况一般用不到
		if (immediate) {
			var callNow = !this.timeout;
			this.timeout = setTimeout(function() {
				this.timeout = null;
			}, wait);
			if (callNow) typeof func === 'function' && func();
		} else {
			// 设置定时器，当最后一次操作后，this.timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
			this.timeout = setTimeout(function() {
				typeof func === 'function' && func();
			}, wait);
		}
	}
	static timer
	static flag
	/**
	 * 节流原理：在一定时间内，只能触发一次
	 * 
	 * @param {Function} func 要执行的回调函数 
	 * @param {Number} wait 延时的时间
	 * @param {Boolean} immediate 是否立即执行
	 * @return null
	 */
	throttle(func, wait = 500, immediate = true) {
		if (immediate) {
			if (!this.flag) {
				this.flag = true;
				// 如果是立即执行，则在wait毫秒内开始时执行
				typeof func === 'function' && func();
				this.timer = setTimeout(() => {
					this.flag = false;
				}, wait);
			}
		} else {
			if (!this.flag) {
				this.flag = true
				// 如果是非立即执行，则在wait毫秒内的结束处执行
				this.timer = setTimeout(() => {
					this.flag = false
					typeof func === 'function' && func();
				}, wait);
			}
			
		}
	};
}